﻿using System;
using System.Collections.Generic;
using System.Linq;
using LINQtoFxCop.Core;
using Microsoft.FxCop.Sdk;

namespace LINQtoFxCop.DomainSpecific.GreyZone
{
  internal sealed class DontIgnoreMethodResults : DomainSpecificIntrospectionRule
  {
    public DontIgnoreMethodResults()
      : base("DontIgnoreMethodResults")
    { }

    protected override IEnumerable<QueryResult> Query()
    {
      return
        from method in Methods
        where ImplementationContainsIgnoredMethodResult(method)
        select Result(method);
    }

    private static bool ImplementationContainsIgnoredMethodResult(Method method)
    {
      var instructions = method.Instructions;
      for (var i = 1; i < instructions.Count; i++)
      {
        if (HasIgnoredMethodResult(instructions[i - 1], instructions[i]))
          return true;
      }
      return false;
    }
    private static bool HasIgnoredMethodResult(Instruction instruction, Instruction nextInstruction)
    {
      switch (instruction.OpCode)
      {
        case OpCode.Call:
        case OpCode.Callvirt:
          if (nextInstruction.OpCode == OpCode.Pop)
          {
            var method3 = (Method)instruction.Value;
            if (IsTryParseMethod(method3))
              return true;
            if (IsStringCreatingMethod(method3))
              return true;
          }
          break;
      }
      return false;
    }
    private static bool IsTryParseMethod(Method method)
    {
      if (method.ReturnType != FrameworkTypes.Boolean)
        return false;

      if (method.Parameters.Count < 2)
        return false;

      if (!method.Parameters[method.Parameters.Count - 1].IsOut)

        return false;

      return method.Name.Name.StartsWith("TryParse", StringComparison.OrdinalIgnoreCase);
    }

    private static bool IsStringCreatingMethod(Method method)
    {
      if (method.DeclaringType == FrameworkTypes.String)
      {
        switch (method.Name.Name)
        {
          case "ToUpper":
          case "ToLower":
          case "Trim":
          case "TrimEnd":
          case "TrimStart":
          case "ToUpperInvariant":
          case "ToLowerInvariant":
          case "Clone":
          case "Format":
          case "Concat":
          case "Copy":
          case "Insert":
          case "Join":
          case "Normalize":
          case "Remove":
          case "Replace":
          case "Split":
          case "PadLeft":
          case "PadRight":
          case "Substring":
            return true;
        }
      }
      return false;
    }
  }
}
